# coding: utf-8
"""
@python version : python3.10
@file name      : auth.py
@date           : 2024/4/28 14:18
@author         : ziyang.yang@aliyun.com
@gitee          : https://gitee.com/ziyangyang1318
@blog           : www.yangziyang.top
@describe       : 
"""
from django.contrib.auth.models import User
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_simplejwt.exceptions import TokenError, InvalidToken
from rest_framework_simplejwt.settings import api_settings
from rest_framework_simplejwt.utils import get_md5_hash_password

from .models import TokenInfo,UserInfo


class LoginAuthentication(BaseAuthentication):
    def authenticate(self, request):
        """
        :param request:
        :return:
        """
        # 方式一 通过请求参数获取传入的token参数
        # token = request.query_params.get('token')
        # # 去数据库查询，token是否存在，如果存在说明登陆过了，如果不存在则表示没有登陆
        # user_token = TokenInfo.objects.filter(token=token).first()
        # if user_token:
        #     return
        # else:
        #     raise AuthenticationFailed('认证失败:用户未登录')
        # 方式二，通过请求头获取
        # print(request.META)
        token = request.META.get('HTTP_TOKEN')
        user_token = TokenInfo.objects.filter(token=token).first()
        if user_token:
            # 要想在登录认证通过之后，从request对象里取出user 和 token 需要返回如下
            user = user_token.user
            return user, token
        else:
            raise AuthenticationFailed('认证失败:用户未登录')


# 自定义jwt 认证类 基于auth.User表认证
class MyJWTAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # 定义请求头中的token直接写，前面不加任何前缀比如jwt
        token = request.META.get('HTTP_AUTHORIZATION', None)
        # 获取请求头中的token
        if token:
            validated_token = self.get_validated_token(token)

            return self.get_user(validated_token), validated_token
        else:
            raise AuthenticationFailed('未携带token')

    def get_validated_token(self, raw_token):
        """
        Validates an encoded JSON web token and returns a validated token
        wrapper object.
        """
        messages = []
        for AuthToken in api_settings.AUTH_TOKEN_CLASSES:
            try:
                return AuthToken(raw_token)
            except TokenError as e:
                messages.append(
                    {
                        "token_class": AuthToken.__name__,
                        "token_type": AuthToken.token_type,
                        "message": e.args[0],
                    }
                )

        raise InvalidToken(
            {
                "detail": "Given token not valid for any token type",
                "messages": messages,
            }
        )

    def get_user(self, validated_token):
        """
        Attempts to find and return a user using the given validated token.
        """
        try:
            user_id = validated_token[api_settings.USER_ID_CLAIM]
        except KeyError:
            raise InvalidToken("Token contained no recognizable user identification")

        try:
            user = User.objects.get(**{api_settings.USER_ID_FIELD: user_id})
        except User.DoesNotExist:
            raise AuthenticationFailed("User not found", code="user_not_found")

        if not user.is_active:
            raise AuthenticationFailed("User is inactive", code="user_inactive")

        if api_settings.CHECK_REVOKE_TOKEN:
            if validated_token.get(
                    api_settings.REVOKE_TOKEN_CLAIM
            ) != get_md5_hash_password(user.password):
                raise AuthenticationFailed(
                    "The user's password has been changed.", code="password_changed"
                )

        return user


# 基于自定义表的token认证类
class UserInfoJWTAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # 定义请求头中的token直接写，前面不加任何前缀比如jwt
        token = request.META.get('HTTP_AUTHORIZATION', None)
        # 获取请求头中的token
        if token:
            validated_token = self.get_validated_token(token)
            # 对请求头中的token 获取payload中的user_id
            user_id = validated_token.get('user_id')
            # print(validated_token.payload) # 获取解码后的明文payload
            # 拿到userid 到数据库中做校验
            # user = UserInfo.objects.get(pk=user_id)
            # 每次接口都会访问数据库，这里可以直接实例化一个对象，将user_id传入，后续操作只使用id就够了
            user = UserInfo(pk=user_id)
            return user, validated_token
        else:
            raise AuthenticationFailed('未携带token')

    def get_validated_token(self, raw_token):
        """
        Validates an encoded JSON web token and returns a validated token
        wrapper object.
        """
        messages = []
        for AuthToken in api_settings.AUTH_TOKEN_CLASSES:
            try:
                return AuthToken(raw_token)
            except TokenError as e:
                messages.append(
                    {
                        "token_class": AuthToken.__name__,
                        "token_type": AuthToken.token_type,
                        "message": e.args[0],
                    }
                )

        raise InvalidToken(
            {
                "detail": "Given token not valid for any token type",
                "messages": messages,
            }
        )